# 网络编程
# 1. TCP/IP简介
# 为了把全世界的所有不同类型的计算机都连接起来，就必须规定一套全球通用的协议，为了实现互联网这个目标，
# 互联网协议簇（Internet Protocol Suite）就是通用协议标准。Internet是由inter和net两个单词组合起来的，
# 原意就是连接“网络”的网络，有了Internet，任何私有网络，只要支持这个协议，就可以联入互联网。
# 因为互联网协议包含了上百种协议标准，但是最重要的两个协议是TCP和IP协议，所以，大家把互联网的协议简称TCP/IP协议。
# IP协议负责把数据从一台计算机通过网络发送到另一台计算机。数据被分割成一小块一小块，然后通过IP包发送出去。
# 由于互联网链路复杂，两台计算机之间经常有多条线路，因此，路由器就负责决定如何把一个IP包转发出去。
# IP包的特点是按块发送，途径多个路由，但不保证能到达，也不保证顺序到达。
# IP地址实际上是一个32位整数（称为IPv4），以字符串表示的IP地址如192.168.0.1实际上是把32位整数按8位分组后的数字表示，目的是便于阅读。
# IPv6地址实际上是一个128位整数，它是目前使用的IPv4的升级版，以字符串表示类似于2001:0db8:85a3:0042:1000:8a2e:0370:7334。
# TCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接，保证数据包按顺序到达。
# TCP协议会通过握手建立连接，然后，对每个IP包编号，确保对方按顺序收到，如果包丢掉了，就自动重发。
# 许多常用的更高级的协议都是建立在TCP协议基础上的，比如用于浏览器的HTTP协议、发送邮件的SMTP协议等。
# 一个TCP报文除了包含要传输的数据外，还包含源IP地址和目标IP地址，源端口和目标端口。
# 端口有什么作用？在两台计算机通信时，只发IP地址是不够的，因为同一台计算机上跑着多个网络程序。
# 一个TCP报文来了之后，到底是交给浏览器还是QQ，就需要端口号来区分。每个网络程序都向操作系统申请唯一的端口号，
# 这样，两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。
# 一个进程也可能同时与多个计算机建立链接，因此它会申请很多端口。

# 2. TCP编程
# Socket是网络编程的一个抽象概念。通常我们用一个Socket表示“打开了一个网络链接”，
# 而打开一个Socket需要知道目标计算机的IP地址和端口号，再指定协议类型即可。
# 客户端: 大多数连接都是可靠的TCP连接。创建TCP连接时，主动发起连接的叫客户端，被动响应连接的叫服务器。
# 导入库
import socket
import os


def client():
    # 创建socket
    # AF_INET指定使用IPv4协议，如果要用更先进的IPv6，就指定为AF_INET6
    # SOCK_STREAM指定使用面向流的TCP协议
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 建立连接
    # 服务器的IP地址和端口号,可以用域名自动转换到IP地址
    # 怎么知道新浪服务器的端口号呢？作为服务器，提供什么样的服务，端口号就必须固定下来。
    # 由于我们想要访问网页，因此新浪提供网页服务的服务器必须把端口号固定在80端口，
    # 因为80端口是Web服务的标准端口。其他服务都有对应的标准端口号，例如SMTP服务是25端口，
    # FTP服务是21端口，等等。端口号小于1024的是Internet标准服务的端口，端口号大于1024的，可以任意使用。
    s.connect(('www.baidu.com', 80))
    # s.connect(('www.dytt8.net', 80))
    # 发送数据
    s.send(b'GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n')
    # TCP连接创建的是双向通道，双方都可以同时给对方发数据。但是谁先发谁后发，怎么协调，要根据具体的协议来决定。
    # 例如，HTTP协议规定客户端必须先发请求给服务器，服务器收到后才发数据给客户端。
    # 发送的文本格式必须符合HTTP标准，如果格式没问题，接下来就可以接收新浪服务器返回的数据了：
    # 接收数据
    buffer = []
    while True:
        d = s.recv(1024)  # 每次读取的字节数
        if d:
            buffer.append(d)
        else:
            break
    data = b''.join(buffer)
    print(data)
    # 关闭连接
    s.close()
    header, html = data.split(b'\r\n\r\n', 1)
    print(header.decode('utf-8'))
    # 把接收的数据写入文件
    dir_path = os.path.join(".", "file")
    file_path = os.path.join(dir_path, 'baidu_main.html')
    with open(file_path, 'wb') as f:
        f.write(html)


# client()

# 服务器
# 和客户端编程相比，服务器编程就要复杂一些。
# 服务器进程首先要绑定一个端口并监听来自其他客户端的连接。如果某个客户端连接过来了，服务器就与该客户端建立Socket连接，随后的通信就靠这个Socket连接了。
# 所以，服务器会打开固定端口（比如80）监听，每来一个客户端连接，就创建该Socket连接。由于服务器会有大量来自客户端的连接，
# 所以，服务器要能够区分一个Socket连接是和哪个客户端绑定的。一个Socket依赖4项：服务器地址、服务器端口、客户端地址、客户端端口来唯一确定一个Socket。
# 但是服务器还需要同时响应多个客户端的请求，所以，每个连接都需要一个新的进程或者新的线程来处理，否则，服务器一次就只能服务一个客户端了。
# 详见tcp_service_demo.py
# 要测试还需一个客户端:  详见tcp_client_demo.py
from basic_demo import tcp_service_demo
tcp_service_demo.service()
# 同一个端口，被一个Socket绑定了以后，就不能被别的Socket绑定了。


# 3. UDP编程
# TCP是建立可靠连接，并且通信双方都可以以流的形式发送数据。
# 相对TCP，UDP则是面向无连接的协议。
# 使用UDP协议时，不需要建立连接，只需要知道对方的IP地址和端口号，就可以直接发数据包。
# 但是，能不能到达就不知道了
# 虽然用UDP传输数据不可靠，但它的优点是和TCP比，速度快，对于不要求可靠到达的数据，就可以使用UDP协议。
# 详见udp_service_demo.py
# 配合客户端udp_client_demo.py
from basic_demo import udp_service_demo

udp_service_demo.udp_service()
